// utils/oss.js
import {
	getName,
	postName
} from '@/common/api.js';

// 分片大小：5MB
const CHUNK_SIZE = 5 * 1024 * 1024;

export async function uploadFileToOSS(info) {
	// 如果文件小于10MB，使用普通上传；大于10MB使用分片上传
	if (info.size < 10 * 1024 * 1024) {
		return await uploadFileNormal(info);
	} else {
		return await uploadFileMultipart(info);
	}
}

// 普通上传（小文件）
async function uploadFileNormal(info) {
	try {
		console.log('普通上传文件路径:', info);

		const ossConfig = await getOSSUploadConfig(info);
		const key = ossConfig.key;

		const result = await uni.uploadFile({
			url: ossConfig.host,
			filePath: info.url,
			name: 'file',
			formData: {
				key: key,
				policy: ossConfig.policy,
				OSSAccessKeyId: ossConfig.accessKeyId,
				signature: ossConfig.signature,
				success_action_status: '200',
			}
		});

		console.log('上传结果:', result);

		let statusCode, data;
		if (Array.isArray(result)) {
			// 回调风格
			const [uploadErr, uploadRes] = result;
			if (uploadErr) {
				throw new Error(`上传失败: ${uploadErr.errMsg}`);
			}
			statusCode = uploadRes.statusCode;
			data = uploadRes.data;
		} else {
			// Promise风格
			statusCode = result.statusCode;
			data = result.data;
		}

		if (statusCode === 200) {
			const fileUrl = `${ossConfig.host}/${key}`;
			console.log('文件上传成功:', fileUrl);

			try {
				await saveFileRecordToDB({
					storagePath: key,
					name: info.name,
					url: fileUrl,
					size: info.size,
					fileHash: info.fileHash,
					totalChunks: 1,
					parent: info.parent || null,
					mimeType: info.tempFile?.type ? info.tempFile?.type : info.type
				});
			} catch (dbError) {
				console.warn('保存文件记录失败:', dbError);
			}

			return fileUrl;
		} else {
			throw new Error(`OSS上传失败，状态码: ${statusCode}`);
		}
	} catch (error) {
		console.error('Upload to OSS failed:', error);
		throw error;
	}
}

// 分片上传（大文件）
async function uploadFileMultipart(info) {
	let uploadId, key, mimeType;

	try {
		console.log('分片上传文件信息:', info);

		// 1. 初始化分片上传
		const initResult = await initMultipartUpload(info);
		uploadId = initResult.uploadId;
		key = initResult.key;

		console.log('初始化成功:', {
			uploadId,
			key
		});

		// 2. 计算分片
		const fileSize = info.size;
		const chunkCount = Math.ceil(fileSize / CHUNK_SIZE);
		const parts = [];

		console.log(`文件大小: ${fileSize}, 分片数: ${chunkCount}, MIME类型: ${info.type}`);

		// 3. 上传所有分片
		for (let i = 0; i < chunkCount; i++) {
			const partNumber = i + 1;
			const start = i * CHUNK_SIZE;
			const end = Math.min(start + CHUNK_SIZE, fileSize);

			console.log(`上传分片 ${partNumber}/${chunkCount}, 范围: ${start}-${end}`);

			try {
				// 获取分片上传URL
				const partData = await getPartUploadUrl(uploadId, key, partNumber, info.type);
				console.log('分片上传URL:', partData.url);

				// 上传分片
				const etag = await uploadPart(partData.url, info, partNumber, start, end, info.type);
				console.log(`分片 ${partNumber} 上传成功，ETag:`, etag);

				parts.push({
					partNumber: partNumber,
					etag: etag
				});

				// 进度更新
				const progress = Math.round((partNumber / chunkCount) * 100);
				console.log(`上传进度: ${progress}%`);

				if (typeof info.onProgress === 'function') {
					info.onProgress(progress);
				}
			} catch (partError) {
				console.error(`分片 ${partNumber} 上传失败:`, partError);
				throw new Error(`分片 ${partNumber} 上传失败: ${partError.message}`);
			}
		}

		// 4. 完成分片上传
		const completeResult = await completeMultipartUpload(uploadId, key, parts);
		console.log('分片上传完成:', completeResult);

		// 5. 保存文件记录
		try {
			await saveFileRecordToDB({
				storagePath: key,
				name: info.name,
				url: completeResult.url,
				size: fileSize,
				fileHash: info.fileHash,
				totalChunks: chunkCount,
				parent: info.parent || null,
				mimeType: info.tempFile?.type ? info.tempFile?.type : info.type
			});
		} catch (dbError) {
			console.warn('保存文件记录失败:', dbError);
		}

		return completeResult.url;

	} catch (error) {
		console.error('分片上传失败:', error);

		// 取消分片上传（清理OSS上的临时文件）
		if (uploadId && key) {
			try {
				await abortMultipartUpload(uploadId, key);
			} catch (abortError) {
				console.warn('取消分片上传失败:', abortError);
			}
		}

		throw error;
	}
}

// 修复后的分片上传函数
async function uploadPart(url, fileInfo, partNumber, chunkStart, chunkEnd, mimeType) {
	return new Promise(async (resolve, reject) => {
		try {
			// 读取文件分片
			const chunkData = await readFileChunk(fileInfo, chunkStart, chunkEnd - chunkStart);

			// console.log(`开始上传分片 ${partNumber}, 大小: ${chunkData.byteLength} bytes`);

			// 使用更兼容的上传方式
			uni.request({
				url: url,
				method: 'PUT',
				data: chunkData,
				header: {
					'Content-Type': ''
				},
				success: (res) => {
					console.log(`分片 ${partNumber} 上传响应:`, {
						statusCode: res.statusCode,
						headers: res.header,
						data: res.data
					});

					if (res.statusCode === 200) {
						// 获取ETag - 阿里云OSS返回的ETag在header中
						const etag = res.header.ETag || res.header.etag || res.header[
							'x-oss-hash-crc64ecma'];
						if (etag) {
							// 移除ETag可能的引号
							const cleanEtag = etag.replace(/"/g, '');
							console.log(`分片 ${partNumber} ETag:`, cleanEtag);
							resolve(cleanEtag);
						} else {
							console.warn('未找到ETag，检查响应头:', res.header);
							// 如果找不到ETag，使用partNumber作为fallback
							resolve(`part-${partNumber}`);
						}
					} else {
						let errorMsg = `分片上传失败，状态码: ${res.statusCode}`;
						if (res.data) {
							// 尝试解析OSS错误信息
							try {
								const parser = new DOMParser();
								const xmlDoc = parser.parseFromString(res.data, "text/xml");
								const errorCode = xmlDoc.getElementsByTagName("Code")[0]
									?.textContent;
								const errorMessage = xmlDoc.getElementsByTagName("Message")[
									0]?.textContent;
								if (errorCode) {
									errorMsg += `, 错误码: ${errorCode}, 信息: ${errorMessage}`;
								}
							} catch (e) {
								errorMsg += `, 响应: ${res.data}`;
							}
						}
						reject(new Error(errorMsg));
					}
				},
				fail: (err) => {
					reject(new Error(`分片上传请求失败: ${err.errMsg}`));
				}
			});

		} catch (error) {
			reject(new Error(`读取文件分片失败: ${error.message}`));
		}
	});
}

// 读取文件分片（跨平台兼容）
function readFileChunk(fileInfo, start, length) {
	return new Promise((resolve, reject) => {
		// #ifdef H5
		// H5环境 - 使用fetch获取文件分片并转换为ArrayBuffer
		readChunkH5(fileInfo, start, length).then(resolve).catch(reject);
		// #endif

		// #ifdef MP-WEIXIN
		// 微信小程序环境 - 使用getFileSystemManager
		readChunkWeixin(fileInfo, start, length).then(resolve).catch(reject);
		// #endif

		// #ifndef H5 || MP-WEIXIN
		// 其他平台 - 使用uni.readFile
		readChunkUni(fileInfo, start, length).then(resolve).catch(reject);
		// #endif
	});
}

// H5环境读取分片 - 修复Blob转ArrayBuffer问题
function readChunkH5(fileInfo, start, length) {
	return new Promise((resolve, reject) => {
		fetch(fileInfo.url)
			.then(response => response.blob())
			.then(blob => {
				const chunkBlob = blob.slice(start, start + length);
				// 将Blob转换为ArrayBuffer
				const reader = new FileReader();
				reader.onload = () => {
					resolve(reader.result);
				};
				reader.onerror = reject;
				reader.readAsArrayBuffer(chunkBlob);
			})
			.catch(reject);
	});
}

// 微信小程序环境读取分片
function readChunkWeixin(fileInfo, start, length) {
	return new Promise((resolve, reject) => {
		const fileSystemManager = uni.getFileSystemManager();
		fileSystemManager.readFile({
			filePath: fileInfo.url,
			position: start,
			length: length,
			success: (res) => {
				resolve(res.data);
			},
			fail: (err) => {
				console.error('微信小程序读取分片失败:', err);
				reject(err);
			}
		});
	});
}

// 通用uni.readFile读取分片
function readChunkUni(fileInfo, start, length) {
	return new Promise((resolve, reject) => {
		uni.readFile({
			filePath: fileInfo.url,
			success: (res) => {
				// 手动截取分片
				const arrayBuffer = res.data;
				const chunk = arrayBuffer.slice(start, start + length);
				resolve(chunk);
			},
			fail: (err) => {
				console.error('uni.readFile读取分片失败:', err);
				reject(err);
			}
		});
	});
}

// 初始化分片上传
async function initMultipartUpload(info) {
	try {
		const result = await postName('/oss/initMultipartUpload', {
			filename: info.name,
			mimeType: info.type,
			size: info.size,
			fileHash: info.fileHash,
		}, {
			custom: {
				auth: true,
				toast: true,
				catch: true,
			},
		});

		return result;
	} catch (error) {
		console.error('初始化分片上传失败:', error);
		throw new Error(`初始化分片上传失败: ${error.message}`);
	}
}

// 获取分片上传URL
async function getPartUploadUrl(uploadId, key, partNumber, mimeType) {
	try {
		const result = await postName('/oss/getPartUploadUrl', {
			uploadId: uploadId,
			key: key,
			partNumber: partNumber,
			mimeType: mimeType
		}, {
			custom: {
				auth: true,
				toast: false,
				catch: true,
			},
		});
		return result;
	} catch (error) {
		console.error('获取分片上传URL失败:', error);
		throw new Error(`获取分片上传URL失败: ${error.message}`);
	}
}

// 完成分片上传
async function completeMultipartUpload(uploadId, key, parts) {
	try {
		const result = await postName('/oss/completeMultipartUpload', {
			uploadId: uploadId,
			key: key,
			parts: parts
		}, {
			custom: {
				auth: true,
				toast: false,
				catch: true,
			},
		});

		return result;
	} catch (error) {
		console.error('完成分片上传失败:', error);
		throw new Error(`完成分片上传失败: ${error.message}`);
	}
}

// 取消分片上传
async function abortMultipartUpload(uploadId, key) {
	try {
		await postName('/oss/abortMultipartUpload', {
			uploadId: uploadId,
			key: key
		}, {
			custom: {
				auth: true,
				toast: false,
				catch: false,
			},
		});
	} catch (error) {
		console.warn('取消分片上传失败:', error);
	}
}

// 保存文件记录
async function saveFileRecordToDB(fileInfo) {
	try {
		const result = await postName('/oss/saveFileRecord', fileInfo, {
			custom: {
				auth: true,
				toast: true,
			},
		});

		return result;
	} catch (error) {
		console.error('保存文件记录失败:', error);
		throw error;
	}
}

// 获取OSS上传配置
async function getOSSUploadConfig(info) {
	try {
		const result = await getName(
			`oss/getOSSToken?filename=${info.name}&fileHash=${info.fileHash}&size=${info.size}`, {
				custom: {
					auth: true,
					toast: false,
					catch: true,
				}
			});
		return result;
	} catch (error) {
		console.error('获取OSS配置失败:', error);
		throw new Error(`获取OSS配置失败: ${error.message}`);
	}
}